package cn.foolishbird.crow.encrypt.springmvc;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.util.Assert;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.AsyncHandlerMethodReturnValueHandler;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.nio.charset.StandardCharsets;
import java.util.Objects;

import static cn.foolishbird.crow.encrypt.springmvc.RSAUtils.RSA_KEY_ALGORITHM;

/**
 * @author foolish-bird
 */
public abstract class SignatureResponseBodyMethodProcessor implements HandlerMethodReturnValueHandler, AsyncHandlerMethodReturnValueHandler {

    private static final Logger log = LoggerFactory.getLogger(SignatureResponseBodyMethodProcessor.class);

    /**
     * appId
     */
    private static final String APP_ID = "appId";

    /**
     * 签名字符串
     */
    private static final String SIGNATURE = "signature";

    /**
     * 求情时间
     */
    private static final String REQUEST_TIMESTAMP = "requestTimestamp";

    /**
     * 随机字符串
     */
    private static final String RANDOM_STRING = "randomString";

    @Override
    public boolean isAsyncReturnValue(Object returnValue, MethodParameter returnType) {
        return supportsReturnType(returnType);
    }

    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
        return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), SignatureResponseBody.class) ||
                returnType.hasMethodAnnotation(SignatureResponseBody.class));
    }

    @Override
    public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
        mavContainer.setRequestHandled(true);
        if (Objects.isNull(returnValue)) {
            return;
        }
        String json = returnJson(returnValue);

        CacheServletServerHttpRequest cache = (CacheServletServerHttpRequest) createInputMessage(webRequest);
        String requestTimestamp = String.valueOf(System.currentTimeMillis());
        String randomString = RandomStringUtils.randomCapitalLetter(32);
        String appId = cache.getServletRequest().getHeader(APP_ID);


        String s = append(requestTimestamp, randomString, appId, json);
        String signature = null;
        try {
            signature = RSAUtils.sign(s.getBytes(StandardCharsets.UTF_8), privateKey(appId), RSA_KEY_ALGORITHM);
        } catch (Exception e) {
            log.error("生成签名失败:{}", e);
            throw new SignatureRuntimeException("生成签名失败");
        }
        log.info("生成签名内容：{}", json);
        log.info("生成签名：{}", signature);
        ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
        outputMessage.getServletResponse().setHeader(REQUEST_TIMESTAMP, requestTimestamp);
        outputMessage.getServletResponse().setHeader(RANDOM_STRING, randomString);
        outputMessage.getServletResponse().setHeader(APP_ID, appId);
        outputMessage.getServletResponse().setHeader(SIGNATURE, signature);
        outputMessage.getServletResponse().setContentType("application/json;charset=utf-8");


        outputMessage.getBody().write(json.getBytes(StandardCharsets.UTF_8));
        outputMessage.flush();
    }


    protected ServletServerHttpResponse createOutputMessage(NativeWebRequest webRequest) {
        HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
        Assert.state(response != null, "No HttpServletResponse");
        return new ServletServerHttpResponse(response);
    }

    protected ServletServerHttpRequest createInputMessage(NativeWebRequest webRequest) {
        HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
        Assert.state(servletRequest != null, "No HttpServletRequest");
        return new CacheServletServerHttpRequest(servletRequest);
    }

    /**
     * 签名字符串拼接规则
     *
     * @param requestTimestamp
     * @param randomString
     * @param appId
     * @param body
     * @return
     */
    private String append(String requestTimestamp, String randomString, String appId, String body) {
        return requestTimestamp + "_" + randomString + "_" + appId + "_" + body;
    }

    /**
     * 私钥
     *
     * @param appId
     * @return
     */
    protected abstract byte[] privateKey(String appId);

    /**
     * 返回对象处理成json
     *
     * @param returnObject
     * @return
     */
    protected abstract String returnJson(Object returnObject);
}
